#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include "../comm/util.hpp"
#include "../comm/log.hpp"

// execute :执行，实施
namespace ns_runner
{
    using namespace ns_util;
    using namespace ns_log;

    class Runner
    {
    public:
        // 设置进程资源大小的窗口
        static void SetProcLimit(int _cpu_limit, int _mem_limit)
        {
            struct rlimit cpu_rlimit;
            cpu_rlimit.rlim_cur = _cpu_limit; // 20M
            cpu_rlimit.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_CPU, &cpu_rlimit);

            struct rlimit mem_rlimit;
            mem_rlimit.rlim_cur = _mem_limit * 1024 * 100; // 我给这里多乘了个100
            mem_rlimit.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_AS, &mem_rlimit);
        }

        // 结果》0 ，说明程序运行时异常了，被信号终止，返回了信号的编码
        // 结果==0,说明程序运行完毕，结果保存到临时文件当中了
        //  返回值《0 ，说明Run内部错误

        // Run只负责跑完执行程序，如果运行时候报错是Run管理，至于程序运行结果的正确性由测试用例决定
        static int Run(const std::string &file_name, int cpu_limit, int mem_limit, char *env[])
        {
            pid_t pid = fork();
            // 先要知道执行程序的文件名
            // 标准输出：
            // 标准输入：程序运行完，结果是什么
            // 标准错误流：程序运行过程中，爆出的错误是什么
            std::string _execute = PathUtil::Exe(file_name);
            std::string _stdin = PathUtil::Stdin(file_name);
            std::string _stdout = PathUtil::Stdout(file_name);
            std::string _stderr = PathUtil::Stderr(file_name);

            umask(0); // 先将文件的权限置为0;
            int _stdin_fd = open(_stdin.c_str(), O_RDONLY | O_CREAT, 0644);
            int _stdout_fd = open(_stdout.c_str(), O_WRONLY | O_CREAT, 0644);
            int _stderr_fd = open(_stderr.c_str(), O_WRONLY | O_CREAT, 0644);

            if (_stdin_fd < 0 || _stderr_fd < 0 || _stdout_fd < 0) // 文件没有打开成功
            {
                LOG(ERROR) << "运行时的标准文件打开失败" << "\n";
                return -1;
            }

            if (pid < 0)
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                LOG(ERROR) << "运行时创建子进程失败" << "\n";
                return -2;
            }
            else if (pid == 0)
            {
                dup2(_stdin_fd, 0);
                dup2(_stdout_fd, 1);
                dup2(_stderr_fd, 2);

                SetProcLimit(cpu_limit, mem_limit);

                execl(_execute.c_str(), _execute.c_str(), nullptr); // 执行的任务,千万不要忘记传递nullptr

                exit(1); // 正确的话，不会执行到这里
            }
            else
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                int status = 0;
                waitpid(pid, &status, 0); // 低7为是信号为，8到15是退出码

                LOG(INFO) << "运行完毕 info: " << (status & 0x7F) << "\n";
                return status & 0x7F; //(1111111)
            }
        }
    };
};
